﻿//////////////////////////////////////////////////////////////////////////////
//
// Copyright © 1998-2024 Glodon Company Limited.
//
// Licensed under the MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the “Software”),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//////////////////////////////////////////////////////////////////////////////
#include "GbmpOffsetActionBehavior.h"
#include "IActionInputEventArgs.h"
#include "IGraphicsCurve3d.h"
#include "IGraphicsNodeReference.h"
#include "IGraphicsNodeGroup.h"
#include "ICurve3d.h"
#include "OffsetUtils.h"
#include "IDocument.h"
#include "IElement.h"
#include "IUserTransaction.h"
#include "UiCommonDialog.h"
#include "IPickFilter.h"
#include "GbmpAllowedElementJudgerForPickFilter.h"
#include "IPickTarget.h"
#include "IEditMode.h"
#include "IReferencePlane.h"
#include "IInstance.h"
#include "IModelLine.h"
#include "IGridNew.h"
#include "ILevel.h"
#include "IGenericElement.h"
#include "IControlDefinition.h"
#include "ICheckBoxGroupDefinition.h"
#include "GUiBaseInterfaceEnums.h"
#include "ILineEditDefinition.h"
#include "GbmpJoinUtils.h"
#include "ElementJoinUtils.h"
#include "IBehaviorConfig.h"
#include "IDrawingAnnotationText.h"
#include "ICommonEditOffsetManager.h"
#include "IElementOffsetStrategy.h"
#include "IGraphicsPolyCurve.h"
#include "IPolyCurve.h"
#include "INotificationManager.h"
#include "INotification.h"
#include "IPlane.h"

#include "EnableCompileWarning_The_LAST_IncludeInCpp.h"

#define ACTION_CHECK_FIXED_OFFSET_GROUP     L"gmCheckFixedOffsetGroup"
#define ACTION_CHECK_FIXED_OFFSET           L"gmCheckFixedOffset"
#define ACTION_EDIT_OFFSET                  L"gmEditOffset"
#define ACTION_CHECK_COPY_GROUP             L"gmCheckCopyGroup"
#define ACTION_CHECK_COPY                   L"gmCheckCopy"
#define ACTION_CHECK_POLYLINE_GROUP         L"gmCheckPolyLineGroup"
#define ACTION_CHECK_POLYLINE               L"gmCheckPolyLine"

namespace gcmp
{
    template<typename T>
    class AllowedElementJudgerForOffsetPickFilter : public gcmp::IGbmpAllowedElementJudgerForPickFilter
    {
    public:
        virtual ~AllowedElementJudgerForOffsetPickFilter() {}
        virtual bool IsAllowedElement(const IElement *pElement) const override
        {
            if (const IGenericElement* pGenericElement = quick_cast<IGenericElement>(pElement))
            {
                return IsA<T>(pGenericElement->GetExternalObject());
            }
            return IsA<T>(pElement);
        }
    };

    class GbmpOffsetPickFilter :public IPickFilter
    {
    public:
        GbmpOffsetPickFilter(gcmp::IDocument * doc);
        ~GbmpOffsetPickFilter();
        virtual bool AllowElement(const gcmp::ElementId& id) const override;
        virtual bool AllowGraphicsNode(const gcmp::IGraphicsNodeReference& nodeReference) const override;
        void SetIsCopyOffsetMode(bool isCopy);
        template<typename T>
        void AddAllowElementType()
        {
            m_arrAllowElementType.push_back(NEW_AS_OWNER_PTR(AllowedElementJudgerForOffsetPickFilter<T>));
        }
        bool SetPickTargetOption(IPickTarget* pickTarget) override;

    private:
        gcmp::IPickTarget *m_pPickTarget;   //能拾取的类型
        std::vector<gcmp::OwnerPtr<IGbmpAllowedElementJudgerForPickFilter>> m_arrAllowElementType;  //支持拾取的Element类型
        bool m_isCopyMode;
        IDocument *m_pDocument;
    };

    GbmpOffsetPickFilter::GbmpOffsetPickFilter(gcmp::IDocument * pDoc)
        : m_pDocument(pDoc)
        , m_isCopyMode(false)
    {

    }

    GbmpOffsetPickFilter::~GbmpOffsetPickFilter(void)
    {
    }

    bool GbmpOffsetPickFilter::AllowElement(const gcmp::ElementId& elementID) const
    {
        IElement* pElement = m_pDocument->GetElement(elementID);

        if (const IGenericElement* pGenericElement = quick_cast<const IGenericElement>(pElement))
        {
            //【二维出图】文字不支持偏移
            if (const IDrawingAnnotationText* pDrawingAnnotationText = dynamic_cast<const IDrawingAnnotationText*>(pGenericElement->GetExternalObject()))
            {
                return false;
            }
        }

        if (const IInstance* pInstance = quick_cast<const IInstance>(pElement))
        {
            //嵌套构件子构件不支持便宜
            if (pInstance->GetSuperInstanceId().IsValid())
                return false;
        }


        IEditMode* pEditModeUE = IEditMode::GetTopActiveEditMode(m_pDocument);
        if (pElement && !m_arrAllowElementType.empty())
        {
            FOR_EACH(pTypeJudger, m_arrAllowElementType)
            {
                if (pTypeJudger->IsAllowedElement(pElement))
                {
                    if (pEditModeUE != nullptr && !pEditModeUE->IsElementEditable(pElement))
                    {
                        return false;
                    }

                    if (!m_isCopyMode && IsA<IReferencePlane>(pElement))
                    {
                        IReferencePlane *pRefPlane = dynamic_cast<IReferencePlane *>(pElement);
                        if (pRefPlane != nullptr && pRefPlane->GetReferencePlaneType() == ReferencePlaneType::BuiltIn)
                        {
                            return false;
                        }
                    }

                    return true;
                }
            }
            return false;
        }
        return true;

    }

    bool GbmpOffsetPickFilter::AllowGraphicsNode(const IGraphicsNodeReference& nodeReference) const
    {
        return true;
    }

    void GbmpOffsetPickFilter::SetIsCopyOffsetMode(bool isCopy)
    {
        m_isCopyMode = isCopy;
    }

    bool GbmpOffsetPickFilter::SetPickTargetOption(IPickTarget* pickTarget)
    {
        pickTarget->EnableGraphicsElementShape();
        return true;
    }

    //////////////////////////////////////////////////////////////////////////

    double GbmpOffsetActionBehavior::S_offsetValue = 1000.0;
    static bool s_isPolyLineElement = false;

    bool GbmpOffsetActionBehavior::DefineInputDialog(std::wstring &title, std::vector<OwnerPtr<gcmp::IControlDefinition>>& inputItems)
    {
        bool isOk = false;

        //固定值偏移
        OwnerPtr<ICheckBoxGroupDefinition> opCheckFixedOffsetGroup = ICheckBoxGroupDefinition::Create(ACTION_CHECK_FIXED_OFFSET_GROUP, L"固定值偏移");
        DBG_WARN_AND_RETURN_FALSE_UNLESS(opCheckFixedOffsetGroup, L"固定值偏移CheckBoxGroup生成失败",L"GDMPLab",L"2024-03-30");
        isOk = opCheckFixedOffsetGroup->AddCheckBox(ACTION_CHECK_FIXED_OFFSET, GBMP_TR(L"固定值偏移"), GBMP_TR(L"固定值偏移"), CheckState::Checked);
        DBG_WARN_AND_RETURN_FALSE_UNLESS(isOk, L"添加CheckBox固定值偏移失败",L"GDMPLab",L"2024-03-30");
        inputItems.push_back(TransferOwnership(opCheckFixedOffsetGroup));

        OwnerPtr<ILineEditDefinition> opEditOffset = ILineEditDefinition::Create(ACTION_EDIT_OFFSET, GBMP_TR(L"偏移值:"), m_dOffsetValue);
        DBG_WARN_AND_RETURN_FALSE_UNLESS(opEditOffset, L"偏移值输入框生成失败",L"GDMPLab",L"2024-03-30");
        inputItems.push_back(TransferOwnership(opEditOffset));

        OwnerPtr<ICheckBoxGroupDefinition> opCheckCopyGroup = ICheckBoxGroupDefinition::Create(ACTION_CHECK_COPY_GROUP, L"复制");
        DBG_WARN_AND_RETURN_FALSE_UNLESS(opCheckCopyGroup, L"复制CheckBoxGroup生成失败",L"GDMPLab",L"2024-03-30");
        isOk = opCheckCopyGroup->AddCheckBox(ACTION_CHECK_COPY, GBMP_TR(L"复制"), GBMP_TR(L"复制"), CheckState::Checked);
        DBG_WARN_AND_RETURN_FALSE_UNLESS(isOk, L"添加CheckBox复制失败",L"GDMPLab",L"2024-03-30");
        inputItems.push_back(TransferOwnership(opCheckCopyGroup));

        OwnerPtr<ICheckBoxGroupDefinition> opCheckPLGroup = ICheckBoxGroupDefinition::Create(ACTION_CHECK_POLYLINE_GROUP, L"多段线");
        DBG_WARN_AND_RETURN_FALSE_UNLESS(opCheckPLGroup, L"CheckBoxGroup生成失败",L"GDMPLab",L"2024-03-30");
        isOk = opCheckPLGroup->AddCheckBox(ACTION_CHECK_POLYLINE, GBMP_TR(L"多段线"), GBMP_TR(L"多段线"), s_isPolyLineElement ? CheckState::Checked : CheckState::Unchecked);
        DBG_WARN_AND_RETURN_FALSE_UNLESS(isOk, L"添加CheckBox多段线失败",L"GDMPLab",L"2024-03-30");
        inputItems.push_back(TransferOwnership(opCheckPLGroup));

        title = GBMP_TR(L"偏移");
        return true;
    }

    bool GbmpOffsetActionBehavior::OnActionInputEvent(IActionInputEventArgs* pArgs)
    {
        const std::pair<std::wstring, gcmp::Any>& msg = pArgs->GetInputMessage();
        const std::wstring eventID = msg.first;
        gcmp::Any inputValue = msg.second;
        m_bActionStateChaged = false;

        std::wstring checkFixedOffsetId, editOffsetId, checkCopyId, checkPLId;
        checkFixedOffsetId = checkFixedOffsetId + ACTION_CHECK_FIXED_OFFSET_GROUP + L"_" + ACTION_CHECK_FIXED_OFFSET;
        editOffsetId = ACTION_EDIT_OFFSET;
        checkCopyId = checkCopyId + ACTION_CHECK_COPY_GROUP + L"_" + ACTION_CHECK_COPY;
        checkPLId = checkPLId + ACTION_CHECK_POLYLINE_GROUP + L"_" + ACTION_CHECK_POLYLINE;
        if (eventID == checkFixedOffsetId)
        {
            m_isFixedOffsetValue = inputValue.cast<bool>();
            m_bActionStateChaged = true;
        }
        else if (eventID == editOffsetId)
        {
            m_dOffsetValue = inputValue.cast<double>();
        }
        else if (eventID == checkCopyId)
        {
            m_isCopyElement = inputValue.cast<bool>();
        }
        else if (eventID == checkPLId)
        {
            m_bActionStateChaged = true;
            s_isPolyLineElement = inputValue.cast<bool>();
        }
        return true;
    }

    bool GbmpOffsetActionBehavior::OnOffsetFinished(gcmp::IDocument* pDoc, const IPlane* pWorkPlane,
        const gcmp::GraphicsNodeReferenceOwnerPtrVector& gNodeReferencesSelection,
        const std::vector<std::pair<ElementId, std::vector<OwnerPtr<ICurve3d>>>>& offsetElementCurves)
    {
        DBG_WARN_AND_RETURN_FALSE_UNLESS(pDoc != nullptr, L"文档为空",L"GDMPLab",L"2024-03-30");
        if (gNodeReferencesSelection.empty() || gNodeReferencesSelection.empty())
            return false;

        DBG_WARN_AND_RETURN_FALSE_UNLESS(gNodeReferencesSelection[0], L"gNodeReferencesSelection[0]为空",L"GDMPLab",L"2024-03-30");
        const IElement *pFirstElement = pDoc->GetElement(gNodeReferencesSelection[0]->GetElementId());
        if (pFirstElement == nullptr)
            return false;
        
        ICommonEditOffsetManager* pOffsetManager = ICommonEditOffsetManager::Get();
        DBG_WARN_AND_RETURN_FALSE_UNLESS(pOffsetManager, L"pOffsetManager为空",L"GDMPLab",L"2024-03-30");
        const IElementOffsetStrategy* pOffsetStrategy = pOffsetManager->GetOrFindParentStrategy(pFirstElement);
        if (pOffsetStrategy)
        {
            if (!pOffsetStrategy->CanBeOffset(pFirstElement, nullptr))
                return false;
        }

        bool bCopy = false;
        GetIsCopyElementOnOffsetFinished(bCopy);

        OwnerPtr<IUserTransaction> opUserTransaction = IUserTransaction::Create(pDoc, GBMP_TR(L"偏移"), true);
        DBG_WARN_AND_RETURN_FALSE_UNLESS(opUserTransaction.get() != nullptr, L"opUserTransaction为空",L"GDMPLab",L"2024-03-30");

        std::vector<ElementId> elementIdsAfterOffset;
        bool bResult = OffsetUtils::OffsetElements(pDoc, pWorkPlane, bCopy, offsetElementCurves, elementIdsAfterOffset);

        FOR_EACH(elementIdAfterOffset, elementIdsAfterOffset)
        {
            ElementJoinUtils::DeleteAllElementJoins(pDoc, elementIdAfterOffset);
            GbmpJoinUtils::CreateJoinsAndCuts(pDoc, elementIdAfterOffset);
        }

        if (bResult)
        {
            opUserTransaction->Commit();
        }
        else
        {
            UiCommonDialog::ShowMessageBox(GBMP_TR(L"偏移"), GBMP_TR(L"偏移操作不满足约束条件！"), (int)UiCommonDialog::ButtonType::OK);
            opUserTransaction->Rollback();
        }
        return true;
    }

    bool gcmp::GbmpOffsetActionBehavior::OnOffsetFinished(gcmp::IDocument * pDocument, const Coordinate3d& coordinate, const gcmp::GraphicsNodeReferenceOwnerPtrVector & gNodeReferencesSelection, const std::vector<OwnerPtr<IPolyCurve>>& offsetPolyCurves)
    {
        DBG_WARN_AND_RETURN_FALSE_UNLESS(pDocument != nullptr, L"文档为空",L"GDMPLab",L"2024-03-30");
        if (offsetPolyCurves.empty() || gNodeReferencesSelection.empty())
            return false;

        DBG_WARN_AND_RETURN_FALSE_UNLESS(gNodeReferencesSelection[0], L"gNodeReferencesSelection[0]为空",L"GDMPLab",L"2024-03-30");
        ElementId elementId = gNodeReferencesSelection[0]->GetElementId();
        const IElement *pFirstElement = pDocument->GetElement(elementId);
        if (pFirstElement == nullptr)
            return false;

        bool bCanBeOffset = false;
        ICommonEditOffsetManager* pOffsetManager = ICommonEditOffsetManager::Get();
        DBG_WARN_AND_RETURN_FALSE_UNLESS(pOffsetManager, L"pOffsetManager为空",L"GDMPLab",L"2024-03-30");
        const IElementOffsetStrategy* pOffsetStrategy = pOffsetManager->GetOrFindParentStrategy(pFirstElement);
        if (pOffsetStrategy)
            bCanBeOffset = pOffsetStrategy->CanBeOffset(pFirstElement, nullptr);
        else
            return false;

        if (!bCanBeOffset)
            return false;

        bool bCopy = false;
        GetIsCopyElementOnOffsetFinished(bCopy);
        OwnerPtr<IPlane> opPlane = IPlane::Create(coordinate.GetOrigin(), coordinate.GetX(), coordinate.GetY());
        DBG_WARN_AND_RETURN_FALSE_UNLESS(opPlane, L"根据多段线所在坐标系创建平面失败",L"GDMPLab",L"2024-03-30");
        OwnerPtr<IUserTransaction> opUserTransaction = IUserTransaction::Create(pDocument, GBMP_TR(L"偏移"), true);
        DBG_WARN_AND_RETURN_FALSE_UNLESS(opUserTransaction != nullptr, L"opUserTransaction为空",L"GDMPLab",L"2024-03-30");

        std::vector<ElementId> elementIdsAfterOffset;
        bool bResult = OffsetUtils::OffsetElements(pDocument, opPlane.get(), bCopy, elementId, offsetPolyCurves, elementIdsAfterOffset);
        if (bResult)
        {
            opUserTransaction->Commit();
        }
        else
        {
            std::vector<OwnerPtr<INotificationDetailItem>> detailItems;
            INotificationManager::Get()->AddNotification(TransferOwnership(INotification::CreateNotification(NotificationTypes::InfomationNotificationType, GBMP_TR(L"偏移操作不满足约束条件！"), std::move(detailItems))));
            opUserTransaction->Rollback();
        }

        return true;
    }


    bool GbmpOffsetActionBehavior::OnUpdatePreview(gcmp::IUiView* pCurrentView,
        const gcmp::Vector3d& mousePoint,
        const std::vector<OwnerPtr<gcmp::ICurve3d>>& opCurves,
        OwnerPtr<IGraphicsNodeGroup>& opGNodeGroup) 
    {
        // 默认预显
        FOR_EACH(opPreviewCurve, opCurves)
        {
            if (opPreviewCurve)
            {
                opGNodeGroup->AddChild(IGraphicsCurve3d::Create(opPreviewCurve.get()));
            }
        }
        return true;
    }


    bool gcmp::GbmpOffsetActionBehavior::OnUpdatePreview(gcmp::IUiView * pCurrentView, const Coordinate3d & coordinate, const gcmp::Vector3d & mousePoint, const std::vector<OwnerPtr<IPolyCurve>>& offsetPolyCurves, OwnerPtr<IGraphicsNodeGroup>& opGNodeGroup)
    {
        // 默认预显
        OwnerPtr<IPlane> opPlane = IPlane::Create(coordinate.GetOrigin(), coordinate.GetX(), coordinate.GetY());
        DBG_WARN_AND_RETURN_FALSE_UNLESS(opPlane, L"根据多段线所在坐标系创建平面失败",L"GDMPLab",L"2024-03-30");
        FOR_EACH(opPreviewCurve, offsetPolyCurves)
        {
            if (opPreviewCurve)
            {
                opGNodeGroup->AddChild(IGraphicsPolyCurve::Create(opPreviewCurve.get(), opPlane.get()));
            }
        }
        return true;
    }

    bool GbmpOffsetActionBehavior::GetIsFixedOffsetValue(bool& isFixed) const
    { 
        isFixed = m_isFixedOffsetValue;
        return true; 
    }

    bool GbmpOffsetActionBehavior::GetFixedOffsetValue(double& offset) const
    { 
        offset = m_dOffsetValue;
        return true; 
    }

    bool GbmpOffsetActionBehavior::GetIsCopyElementOnOffsetFinished(bool& isCopy) const
    { 
        isCopy = m_isCopyElement;
        return true;
    }

    bool GbmpOffsetActionBehavior::GetIsPolyLineElementOnOffsetFinished(bool& isPolyLine) const
    {
        isPolyLine = s_isPolyLineElement;
        return true;
    }

    bool GbmpOffsetActionBehavior::GetIsActionStateChanged() const
    {
        return m_bActionStateChaged;
    }

    OwnerPtr<IPickFilter> gcmp::GbmpOffsetActionBehavior::GetOffsetPickFilter(IDocument * pDocument) const
    {
        OwnerPtr<GbmpOffsetPickFilter> opOffsetSnapFilter = NEW_AS_OWNER_PTR(GbmpOffsetPickFilter, pDocument);

        bool isCopy(true);
        GetIsCopyElementOnOffsetFinished(isCopy);

        if (isCopy)
        {
            opOffsetSnapFilter->SetIsCopyOffsetMode(true);
        }
        else
        {
            opOffsetSnapFilter->SetIsCopyOffsetMode(false);
        }

        opOffsetSnapFilter->AddAllowElementType<IInstance>();
        opOffsetSnapFilter->AddAllowElementType<IGridNew>();
        opOffsetSnapFilter->AddAllowElementType<ILevel>();
        opOffsetSnapFilter->AddAllowElementType<IModelLine>();
        opOffsetSnapFilter->AddAllowElementType<IReferencePlane>();

        return TransferOwnership(opOffsetSnapFilter);
    }

    GbmpOffsetActionBehavior::GbmpOffsetActionBehavior()
        : m_isFixedOffsetValue(true)
        , m_isCopyElement(true)
        , m_bActionStateChaged(false)
    {
        if (IBehaviorConfig::GetConfigRecordValueAsString(L"KeepPreEditValue") == L"true")
        {
            m_dOffsetValue = GbmpOffsetActionBehavior::S_offsetValue;
        }
        else
        {
            m_dOffsetValue = 1000.0;
        }
    }

    GbmpOffsetActionBehavior::~GbmpOffsetActionBehavior()
    {
        if (IBehaviorConfig::GetConfigRecordValueAsString(L"KeepPreEditValue") == L"true")
        {
            GbmpOffsetActionBehavior::S_offsetValue = m_dOffsetValue;
        }
    }

}

